package net.m2technologies.open_arm.utilities.generic_object_functions;

import net.m2technologies.open_arm.utilities.DateCalendarHelper;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * Copyright 2005 Mark Masterson<br> <br> Licensed under the Apache License, Version 2.0 (the "License");<br> you may
 * not use this file except in compliance with the License.<br> You may obtain a copy of the License at<br> <br>
 * http://www.apache.org/licenses/LICENSE-2.0<br> <br> Unless required by applicable law or agreed to in writing,
 * software<br> distributed under the License is distributed on an "AS IS" BASIS,<br> WITHOUT WARRANTIES OR CONDITIONS
 * OF ANY KIND, either express or implied.<br> See the License for the specific language governing permissions and<br>
 * limitations under the License.<br>
 * <p/>
 * <p>Description: </p>
 *
 * @author Mark Masterson
 * @version 0.010
 */
class ReflectiveWorker {

    private static final Field[] ZERO_LENGTH_FIELD_ARRAY = new Field[0];
    private static final int STRING_BUFFER_PADDING = 1024;
    private static final char TO_STRING_RIGHT_BRACKET = '[';
    private static final char TO_STRING_LEFT_BRACKET = ']';

    public String getFieldValueAsString(final Field fieldObj, final Object object) {
        String result;
        try {
            final Object value = fieldObj.get(object);
            result = getObjectValueAsString(value);
            return result;
        } catch (Exception ex) {
            result = "???";
            return result;
        }
    }

    /**
     * Reads the value of a given fieldname out of a given object and returns it as a string.
     *
     * @param object
     * @param fieldName
     *
     * @return fieldValue
     */
    protected String getSpecialFieldValue(final Object object, final String fieldName) {
        if (null == object || null == fieldName || "".equals(fieldName)) return null;

        final String objectContents = (new GenericObjectFunctions()).toString(object);
        String fieldValue = null;
        int beginIndex;
        final int endIndex;
        beginIndex
                = objectContents.indexOf(
                new StringBuffer().append(fieldName).append(ToStringWorker.TO_STRING_EQUALS_CHARACTER).toString());
        if (-1 != beginIndex) {
            beginIndex = beginIndex + fieldName.length() + 1;
            fieldValue = objectContents.substring(beginIndex);
            endIndex = fieldValue.indexOf("|");
            if (-1 != endIndex) {
                fieldValue = fieldValue.substring(0, endIndex);
            }
        }
        return fieldValue;
    }

    protected String getObjectValueAsString(final Object value) {
        String result;
        final Object[] valueArray = isObjectArray(value);
        if (null != valueArray) {
            result = toStringBuffer(valueArray).toString();
        } else {
            if (value instanceof Calendar) {
                result = DateCalendarHelper.getCalendarAsString((Calendar) value);
            } else if (value instanceof java.util.Date) {
                result = DateCalendarHelper.getDateAsString((java.util.Date) value);
            } else {
                result = value.toString();
            }
        }
        if (null == result) result = "???";
        return result;
    }

    //if the Object passed is an array, cast it to one.  If the array is
    //made up of primitives, convert it to an array of the corresponding
    //object -- for example, an int[] becomes an Integer[]... Return the
    //resulting Object array.
    protected Object[] isObjectArray(final Object object) {
        Object[] result = null;
        final AbstractPrimitiveObjectArrayWrapper wrapper
                = (new PrimitiveObjectArrayWrapperFactory()).getInstance(object);
        if (null != wrapper) {
            //Won't be null if the original object was an array of primitives...
            result = wrapper.convertObjectToObjectArray(object);
        } else {
            //If no wrapper was found...
            if (object.getClass().isArray()) {
                //... but it IS an array, then it's already an Object[] -- do the cast...
                result = (Object[]) object;
            }
        }
        return result;
    }

    //get an object's declared fields, make them "accessible", and filter out
    //the static ones...
    protected Field[] getAccessibleFields(final Class clazz) throws SecurityException {
        final Field[] fields = clazz.getDeclaredFields();
        AccessibleObject.setAccessible(fields, true);
        final List nonStaticFields = new ArrayList();
        for (int i = 0; i < fields.length; i++) {
            final Field einField = fields[i];
            if (!Modifier.isStatic(einField.getModifiers())) {
                nonStaticFields.add(einField);
            }
        }
        return (Field[]) nonStaticFields.toArray(ZERO_LENGTH_FIELD_ARRAY);
    }

    //take an array and return a comma-delimited StringBuffer of its contents
    protected StringBuffer toStringBuffer(final Object[] array) {
        //define a variable that is either: the value of array.length, or
        //zero if the array parameter is null...
        final int arrayLaenge = null != array ? array.length : 0;
        //make the buffer large enough for all of the elements of the array,
        //plus a comma between each element, plus the brackets at begin and
        //end, plus (hopefully) enough room for the characters of each element...
        final StringBuffer einBuffer = new StringBuffer(2 * arrayLaenge
                                                        + 2
                                                        + STRING_BUFFER_PADDING);
        //loop through the elements, and make a StringBuffer for output that
        //is formatted like a java.util.Collection.toString(), i.e.,
        // "[element, element, element]"
        einBuffer.append(TO_STRING_RIGHT_BRACKET);
        for (int i = 0; i < arrayLaenge; i++) {
            final Object temp = array[i];
            //ignore elements that are null...
            if (null != temp) {
                //deal recursively with the possibility that an element of an
                //array might itself also be an array...
                final Object[] tempArray = isObjectArray(temp);
                if (null != tempArray && tempArray != temp) {
                    einBuffer.append(toStringBuffer(tempArray).toString());
                } else {
                    einBuffer.append(temp.toString());
                }
                if (i != arrayLaenge - 1) {
                    einBuffer.append(", ");
                }
            }
        }
        einBuffer.append(TO_STRING_LEFT_BRACKET);
        return einBuffer;
    }
}
